package ascript
{
	//#########################
	// Header Includes
	//#########################
	import ascript.Game;
	import ascript.GameAssets;
	import ascript.utilities.PointSimple;
	import ascript.utilities.SoundChannelQueued;
	import ascript.utilities.SoundTemplate;
	import flash.media.Sound;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	
	//#########################
	// Class Declaration
	//#########################
	public class GameSound
	{
		//=========================
		// Variables
		//=========================
		// Protected
		protected var soundName:String;
		protected var soundChannels:Vector.<SoundChannelQueued> = new Vector.<SoundChannelQueued>;
		protected var soundChannelsLength:uint;
		protected var soundRequests:Vector.<SoundTemplate> = new Vector.<SoundTemplate>;
		protected var soundRequestsLength:uint;
		protected var soundTemplates:Dictionary = new Dictionary();
		protected var soundDeletedRequests:Vector.<uint> = new Vector.<uint>;
		protected var soundDeletedRequestsLength:uint;
		
		//=========================
		// Initialization
		//=========================	
		public function GameSound( tGame:Game, tName:String ):void
		{
			// Set name and load data from Assets
			soundName = tName;
			loadDataSound( GameAssets.getAsset( tName ) );
		}
		
		protected function loadDataSound( tData:XML ):void
		{
			if ( tData != null )
			{
				// Temp Variables
				var tempItem:XML;
				var tempSound:Sound;
				
				tData.ignoreWhitespace = true;
				
				// Load Channels
				soundChannelsLength = new uint( tData.channelCount.@value );
				for ( var i:uint = 0; i < soundChannelsLength; ++i )
				{
					soundChannels.push( new SoundChannelQueued() );
				}
				
				// Load Templates
				for each ( tempItem in tData.Template )
				{
					tempSound = GameAssets.getAsset( tempItem.@soundName );
					if ( tempSound != null )
					{
						soundTemplates[ tempItem.@soundName ] = new SoundTemplate( tempSound, new PointSimple( uint( tempItem.@channelRangeMin ), uint( tempItem.@channelRangeMax ) ), Boolean( uint( tempItem.@isForced ) ), Boolean( uint( tempItem.@isGameTime ) ), tempItem.@delay, tempItem.@duration, tempItem.@volume );
					}
				}
			}
		}
		
		//=========================
		// Destruction
		//=========================	
		public function destroySound():void
		{
			// Temp Variables
			var i:uint;
			
			// Clear memory
			for ( i = 0; i < soundChannels.length; ++i )
			{
				soundChannels[i].stopChannel();
			}
			soundChannels.length = 0;
			soundTemplates.length = 0;
		}
		
		//=========================
		// Request
		//=========================
		public function requestSound( tRequest:String ):void
		{
			if ( soundTemplates[ tRequest ] != null )
			{
				soundRequests.push( soundTemplates[ tRequest ].cloneTemplate );
				soundRequests[ soundRequestsLength ].delay = ( soundRequests[ soundRequestsLength ].delay * 1000 ) + getTimer(); // converts delay to milliseconds plus current time
				soundRequests[ soundRequestsLength ].duration *= 1000; // converts duration to milliseconds
				soundRequestsLength += 1;
			}
		}
		
		public function stopChannelsSound( tChannelRange:PointSimple ):void
		{
			// Temp Variables
			var tempMax:uint = PointSimple.clamp( tChannelRange.y, 0, ( soundChannelsLength - 1 ) );
			
			for ( var i:uint = PointSimple.clamp( tChannelRange.x, 0, ( soundChannelsLength - 1 ) ); i <= tempMax; ++i )
			{
				soundChannels[i].stopChannel();
			}
		}
		
		//=========================
		// Tick
		//=========================
		public function updateSound( tGame:Game, tGameTime:int, tRealTime:int ):void
		{
			// Temp Variables
			var i:int
			var tempTime:int;
			var tempIsAllowPlay:Boolean;
			
			// Reset check variables
			if ( soundDeletedRequestsLength != 0 )
			{
				soundDeletedRequestsLength = 0;
				soundDeletedRequests.length = soundDeletedRequestsLength;
			}
				
			// First pass to identify deleted and play sounds
			for ( i = ( soundRequestsLength - 1 ); i >= 0; i-- )
			{
				// Only process if the template is not running on game time or if it is and the game is not paused
				if ( !soundRequests[i].isGameTime || ( soundRequests[i].isGameTime && !tGame.paused ) )
				{
					// Adjust time
					tempTime = tRealTime;
					if ( soundRequests[i].isGameTime )
					{
						tempTime = tGameTime;
					}
					
					// Process
					if ( soundRequests[i].delay <= tempTime )
					{
						tempIsAllowPlay = true;
						
						// Trash infinite loop request or if past their duration (even if they don't get a chance to play)
						if ( soundRequests[i].duration <= 0 || ( soundRequests[i].delay + soundRequests[i].duration ) <= tempTime )
						{
							soundChannelsActiveTemplates[ soundRequests[i].channel ] = 255;
							soundDeletedRequests.push( i );
							soundDeletedRequestsLength += 1;
							
							// Disable possibility of playing if not looper
							if ( soundRequests[i].duration > 0 )
							{
								tempIsAllowPlay = false;
							}
						}
					
						// Try to play sounds if sound channel is open, is forced open with a new template or the sound is looping
						if ( tempIsAllowPlay && ( soundChannelsActiveTemplates[ soundRequests[i].channel ] == 255 || ( soundRequests[i].isForced && soundChannelsActiveTemplates[ soundRequests[i].channel ] != soundRequests[i].templateIndex ) ) )
						{
							soundChannelsActiveTemplates[ soundRequests[i].channel ] = soundRequests[i].templateIndex;
							
							if ( worldinfo.NetMode != NM_DedicatedServer )
							{
								soundChannels[ soundRequests[i].channel ].Stop(); // stop just incase
								soundChannels[ soundRequests[i].channel ].SoundCue = soundRequests[i].sound;
								soundChannels[ soundRequests[i].channel ].FadeIn( soundRequests[i].durationFadeIn, 1 ); // fades in channel with duration of 0 meaning immediate play
							}
						}
					}
				}
			}
			
			// Second pass to delete and stop sounds
			if ( soundDeletedRequestsLength > 0 )
			{
				for ( i = 0; i < soundDeletedRequestsLength; i++ )
				{
					// Fadeout only if channel was just turned off
					if ( worldinfo.NetMode != NM_DedicatedServer && soundChannelsActiveTemplates[ soundRequests[ soundDeletedRequests[i] ].channel ] == 255 )
					{
						soundChannels[ soundRequests[ soundDeletedRequests[i] ].channel ].FadeOut( soundRequests[ soundDeletedRequests[i] ].durationFadeOut, 0 );
					}
					
					setRequestSound( soundRequests[ soundDeletedRequests[i] ], false, soundDeletedRequests[i] );
				}
			}
		}
	}
}